﻿namespace DCHealthCheck.Tests {

    #region Usings
    using DCHealthCheck.DomainModel;
    using DCHealthCheck.Common;
    using Microsoft.VisualStudio.TestTools.UnitTesting;
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Management;
    using System.Text;
    using System.Threading.Tasks;
    using System.Diagnostics; 
    #endregion

    [TestClass]
    public class Tests {

        private static readonly TimeSpan wmiSearcherTimeout = new TimeSpan(0, 2, 0);

        [TestMethod]
        public void TestExcludedServer() {

            // Arrange
            var excludedServer = new ExcludedServer() {
                Description = "SomeDescription",
                DN = "CN=ServerName,CN=DomainControllers,DC=acme,DC=com",
                SamAccountName = "DC1"
            };

            //Assert
            Assert.IsNotNull(excludedServer);
            Assert.IsTrue(excludedServer.IsValid);

            excludedServer.DN = string.Empty;
            excludedServer.SamAccountName = string.Empty;
            Assert.IsFalse(excludedServer.IsValid);
        }

        [TestMethod]
        public void TestNtpClient() {
            Debug.WriteLine("{0}", DateTime.Now.ToString());

            // Arrange
            var ntpClient = new NTPClient("time.windows.com");

            // Act
            ntpClient.Connect();

            // Assert 
            Assert.IsTrue(ntpClient.IsResponseValid);

            Debug.WriteLine("{0} Results: ", DateTime.Now.ToString());
            Debug.WriteLine(ntpClient.ToString());
        }

        [TestMethod]
        public void TestProcessInfoBuilder() {
            Debug.WriteLine("{0}", DateTime.Now.ToString());

            // Arrange
            var managementObjects = new List<ManagementObject>();

            var scope = new ManagementScope(string.Format(@"\\{0}\root\CIMV2", "localhost"));
            var query = new ObjectQuery(DCHealthCheck.SystemInfoBuilder.ProcessWmiQueryFilter);
            using (var searcher = new ManagementObjectSearcher(scope, query)) {
                searcher.Options.Timeout = wmiSearcherTimeout;

                // Act
                var managementObjectCollection = searcher.Get();

                // Assert
                Assert.IsTrue(managementObjectCollection.Count > 0);


                foreach (ManagementObject managementObject in managementObjectCollection) {
                    managementObjects.Add(managementObject);
                }
            }

            var processManagementObject = managementObjects
                .Where(x =>
                    ((x["ProcessId"] != null) && (Convert.ToInt64(x["ProcessId"]) > 4)) &&
                    ((x["ExecutablePath"] != null) || (x["CommandLine"] != null))
                ).FirstOrDefault();
            var processInfo = new ProcessInfo() { DetachedState = DetachedState.Added };
            var processInfoBuilder = new ProcessInfoBuilder(processInfo, processManagementObject, DateTime.Now);
            processInfo = processInfoBuilder.GetProcessInfo();

            Assert.IsNotNull(processInfo);
            var validationResults = processInfo.Validate(null).ToList();
            if (validationResults.Where(x => x.MemberNames.Any(y => (y != "Id") && (y != "SystemInfo") && (y != "SystemInfoId"))).Count() > 0) {
                Debug.WriteLine("{0} validation errors:", DateTime.Now.ToString());
                foreach (var result in validationResults) {
                    Debug.WriteLine("- Property(s): {0} Validation Error Message: {1}",
                        result.MemberNames.ToList().ToDelimitedString(), result.ErrorMessage);
                }
            }

            Assert.IsTrue(validationResults.Where(x => x.MemberNames.Any(y => (y != "Id") && (y != "SystemInfo") && (y != "SystemInfoId"))).Count() == 0);
            Debug.WriteLine("Process used for test: {0}", processInfo.ToString());
        }

        [TestMethod]
        public void TestServiceInfoBuilder() {
            Debug.WriteLine("{0}", DateTime.Now.ToString());

            // Arrange
            var managementObjects = new List<ManagementObject>();

            var scope = new ManagementScope(string.Format(@"\\{0}\root\CIMV2", "localhost"));
            var query = new ObjectQuery(DCHealthCheck.SystemInfoBuilder.ServiceWmiQueryFilter);
            using (var searcher = new ManagementObjectSearcher(scope, query)) {
                searcher.Options.Timeout = wmiSearcherTimeout;

                // Act
                var managementObjectCollection = searcher.Get();

                // Assert
                Assert.IsTrue(managementObjectCollection.Count > 0);


                foreach (ManagementObject managementObject in managementObjectCollection) {
                    managementObjects.Add(managementObject);
                }
            }

            var serviceManagementObject = managementObjects
                .Where(x =>
                    (x["DisplayName"] != null)
                ).FirstOrDefault();

            var serviceInfo = new ServiceInfo() { DetachedState = DetachedState.Added };
            var serviceInfoBuilder = new ServiceInfoBuilder(serviceInfo, serviceManagementObject);
            serviceInfo = serviceInfoBuilder.GetServiceInfo();

            Assert.IsNotNull(serviceInfo);
            var validationResults = serviceInfo.Validate(null).ToList();
            if (validationResults.Where(x => x.MemberNames.Any(y => (y != "Id") && (y != "SystemInfo") && (y != "SystemInfoId"))).Count() > 0) {
                Debug.WriteLine("{0} validation errors:", DateTime.Now.ToString());
                foreach (var result in validationResults) {
                    Debug.WriteLine("- Property(s): {0} Validation Error Message: {1}",
                        result.MemberNames.ToList().ToDelimitedString(), result.ErrorMessage);
                }
            }

            Assert.IsTrue(validationResults.Where(x => x.MemberNames.Any(y => (y != "Id") && (y != "SystemInfo") && (y != "SystemInfoId"))).Count() == 0);
            Debug.WriteLine("Service used for test: {0}", serviceInfo.ToString());
        }

        /// <summary>
        /// Requires administrator permission.  May require a long time (5+ minutes) on large domains/forests.
        /// </summary>
        [TestMethod]
        public void TestStartupDataGenerator() {
            Debug.WriteLine("{0}", DateTime.Now.ToString());
            DateTime startTime = DateTime.UtcNow;

            // Arrange
            string dcHealthCheckTagRDN = "CN=DCHealthCheckTag,CN=Users";

            // Act
            var FSMORoleOwners = StartupDataGenerator.GetFSMORoleOwners();
            var DomainGPOs = StartupDataGenerator.GetDomainGPOs();
            var DCComputerAccounts = StartupDataGenerator.GetDCComputerAccounts();

            List<TagObject> tagObjects = StartupDataGenerator.GetTagObjects(dcHealthCheckTagRDN);
            var DomainPDCSysvolPolicyFolders = StartupDataGenerator.GetPDCSysvolTestFolders();
            var ADSites = StartupDataGenerator.GetADSites();

            // Assert
            Assert.IsTrue(FSMORoleOwners.Count > 0);
            Assert.IsTrue(DomainGPOs.Count > 0);
            Assert.IsTrue(DCComputerAccounts.Count > 0);
            // tage objects may not have been created yet.
            //Assert.IsTrue(tagObjects.Count > 0);
            Assert.IsTrue(DomainPDCSysvolPolicyFolders.Count > 0);
            Assert.IsTrue(ADSites.Count > 0);

            Debug.WriteLine("Time required: {0}", DateTime.UtcNow.Subtract(startTime).ToString());
        }

        /// <summary>
        /// Requires administrator permission
        /// Use the following to configure the local machine if not a domain member:
        /// w32tm /config /syncfromflags:manual /manualpeerlist:time.windows.com /update
        /// </summary>
        [TestMethod]
        public void TestTimeSyncInfoBuilder() {
            Debug.WriteLine("{0}", DateTime.Now.ToString());

            // Arrange
            TimeSyncInfoBuilder timeSyncInfoBuilder = null;
            var timeSyncInfo = new TimeSyncInfo() { DetachedState = DetachedState.Added };

            timeSyncInfoBuilder = new TimeSyncInfoBuilder(timeSyncInfo, "localhost");

            // Act
            timeSyncInfo = timeSyncInfoBuilder.GetTimeSyncInfo();

            // Assert
            Assert.IsNotNull(timeSyncInfo);
            var validationResults = timeSyncInfo.Validate(null).ToList();
            if (validationResults.Where(x => x.MemberNames.Any(y => (y != "Id") && (y != "SystemInfo") && (y != "SystemInfoId"))).Count() > 0) {
                Debug.WriteLine("{0} validation errors:", DateTime.Now.ToString());
                foreach (var result in validationResults) {
                    Debug.WriteLine("- Property(s): {0} Validation Error Message: {1}",
                        result.MemberNames.ToList().ToDelimitedString(), result.ErrorMessage);
                }
            }

            Assert.IsTrue(validationResults.Where(x => x.MemberNames.Any(y => (y != "Id") && (y != "SystemInfo") && (y != "SystemInfoId"))).Count() == 0);
        }

        [TestMethod]
        public void TestWmiSystemInfoBuilder() {
            Debug.WriteLine("{0}", DateTime.Now.ToString());

            // Arrange
            var domainController = new DomainController() {
                DNSName = "localhost"
            };
            var systemInfo = new SystemInfo() {
                DomainController = domainController
            };
            var systemWmiInfoBuilder = new SystemWmiInfoBuilder(systemInfo);

            // Act
            systemWmiInfoBuilder.GetSystemWmiInfo();

            // Assert
            Assert.IsTrue(systemInfo.StartupDateTimeUTC > DateTime.MinValue);
            Assert.IsTrue(systemInfo.ProcessCount > 0);
            Assert.IsTrue(systemInfo.CDriveTotalMB > 0);
            Assert.IsTrue(systemInfo.CPUCount > 0);
        }
    }
}
